#include "mainwindow.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSplitter>
#include <QFileDialog>
#include <QPixmap>
#include <QScrollArea>
#include <QDebug>
#include <QComboBox>
#include <QMessageBox>
#define MAXSIZE 1500
MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
{
    setWindowTitle("图像上传与文本设置界面");
    resize(1200, 800);
    // 创建一个新的 QWidget 作为左侧区域的容器
    QWidget *leftWidget = new QWidget;

    // 创建一个垂直布局管理器，负责控制左侧区域中控件的垂直排列
    QVBoxLayout *leftLayout = new QVBoxLayout(leftWidget);
    // 创建一个按钮，用于上传图像1
    QPushButton *btnLoad1 = new QPushButton("上传图像1");
    // 创建一个 QLabel 用于显示图像1，初始显示文字
    // imageLabel1 = new QLabel("图像1区域");
    imageLabel1 = new ImageLabel;

    // 设置图像显示居中对齐
    imageLabel1->setAlignment(Qt::AlignCenter);
    // 设置图像标签的尺寸策略为 Ignored，意味着它的尺寸可以被拉伸以适应父控件
    imageLabel1->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    // 设置图像标签内容可缩放以适应标签大小（开启缩放）
    imageLabel1->setScaledContents(true);
    // 为图像标签添加灰色边框，方便观察显示区域
    imageLabel1->setStyleSheet("border: 1px solid gray;");
    // 创建一个 QScrollArea 用于包装图像标签，实现滚动显示
    scrollArea1 = new QScrollArea;
    // 设置 QScrollArea 中显示的子控件为图像标签
    scrollArea1->setWidget(imageLabel1);
    // 启用自动调整 scrollArea 内容大小以适应图像变化
    scrollArea1->setWidgetResizable(true);

    // 将上传按钮添加到左侧布局中
    leftLayout->addWidget(btnLoad1);
    // 将图像显示区域（滚动区域）添加到左侧布局中
    leftLayout->addWidget(scrollArea1);


    // 中间图2上传
    QWidget *centerWidget = new QWidget;
    QVBoxLayout *centerLayout = new QVBoxLayout(centerWidget);
    QPushButton *btnLoad2 = new QPushButton("上传图像2");
    // imageLabel2 = new QLabel("图像2区域");
    imageLabel2 = new ImageLabel;
    imageLabel2->setAlignment(Qt::AlignCenter);
    imageLabel2->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    imageLabel2->setScaledContents(true);
    imageLabel2->setStyleSheet("border: 1px solid gray;");

    scrollArea2 = new QScrollArea;
    scrollArea2->setWidget(imageLabel2);
    scrollArea2->setWidgetResizable(true);
    centerLayout->addWidget(btnLoad2);
    centerLayout->addWidget(scrollArea2);


    // 右侧文本设置
    QWidget *rightWidget = new QWidget;
    QVBoxLayout *rightLayout = new QVBoxLayout(rightWidget);
    // 设置一个按钮，清除标注
    QPushButton *clearButton = new QPushButton("清除标注");
    // 设置一个OCR识别的按钮
    QPushButton *ocrButton = new QPushButton("OCR识别");
    // 设置一个下拉框分别为语言选择，设置中文、英文、日文、韩文、德文、法文几个选项。默认是中文
    QComboBox *languageComboBox = new QComboBox;
    languageComboBox->addItem("中文");
    languageComboBox->addItem("英文");
    languageComboBox->addItem("日文");
    languageComboBox->addItem("韩文");
    languageComboBox->addItem("德文");
    languageComboBox->addItem("法文");
    languageComboBox->setCurrentIndex(0);

    rightLayout->addWidget(clearButton);
    rightLayout->addWidget(ocrButton);
    rightLayout->addWidget(languageComboBox);

    QLabel *label = new QLabel("文本设置：");
    textEditor = new QTextEdit;
    textEditor->setPlaceholderText("在此输入设置内容...");
    textEditor->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    rightLayout->addWidget(label);
    rightLayout->addWidget(textEditor);

    // 使用 QSplitter 实现三栏布局，支持自动拉伸
    QSplitter *mainSplitter = new QSplitter(Qt::Horizontal);
    mainSplitter->addWidget(leftWidget);
    mainSplitter->addWidget(centerWidget);
    mainSplitter->addWidget(rightWidget);

    mainSplitter->setStretchFactor(0, 9);  // 左边 stretch 4
    mainSplitter->setStretchFactor(1, 9);  // 中间 stretch 4
    mainSplitter->setStretchFactor(2, 2);  // 右边 stretch 2


    setCentralWidget(mainSplitter);
    connect(imageLabel1, &ImageLabel::pointsUpdated, this, &MainWindow::matchAndMapPoints);
    // 连接上传按钮的点击信号到对应的槽函数 loadImage1，用于选择图像
    connect(btnLoad1, &QPushButton::clicked, this, &MainWindow::loadImage1);
    // 按钮2和相关函数进行连接
    connect(btnLoad2, &QPushButton::clicked, this, &MainWindow::loadImage2);
    // 清空按钮触发
    connect(clearButton, &QPushButton::clicked, this,&MainWindow::clearButtonfunction);
}


MainWindow::~MainWindow() {}


cv::Mat QPixmapToCvMat(const QPixmap& pixmap) {
    QImage img = pixmap.toImage().convertToFormat(QImage::Format_RGB888);
    return cv::Mat(img.height(), img.width(), CV_8UC3, (void*)img.bits(), img.bytesPerLine()).clone();
}

void MainWindow::loadImage1() {
    // 打开文件选择对话框，让用户选择图像1文件（支持 png, jpg, jpeg 格式）
    QString fileName = QFileDialog::getOpenFileName(
            this,                    // 父窗口指针
            "选择图像1",             // 对话框标题
            "",                      // 默认打开路径（空表示使用默认）
            "Images (*.png *.jpg *.jpeg)" // 文件过滤器：只显示常见图像类型
    );
    // 如果用户选择了图像（文件路径不为空）
    if (!fileName.isEmpty()) {
        // 使用选中的文件路径创建 QPixmap 对象并保存到成员变量 originalPixmap1 中
        originalPixmap1 = QPixmap(fileName);

        // 调整尺寸大小,如果尺寸的宽高大于1500,那么就除以2
        while (originalPixmap1.width() > MAXSIZE || originalPixmap1.height() > MAXSIZE) {
            originalPixmap1 = originalPixmap1.scaled(
                    originalPixmap1.width() / 2, originalPixmap1.height() / 2,
                    Qt::KeepAspectRatio, Qt::SmoothTransformation);
        }
        // 将图像传递给 ImageLabel 控件中的自定义函数 setOriginalPixmap，用于缩放显示和标注
        imageLabel1->setOriginalPixmap(originalPixmap1);
        // 将 QPixmap 类型图像转换为 OpenCV 中的 cv::Mat（格式为 BGR）
        cv::Mat img1 = QPixmapToCvMat(originalPixmap1);


        // 将图像转换为灰度图像（SIFT 只需要灰度图像作为输入）
        cv::cvtColor(img1, img1_gray, cv::COLOR_BGR2GRAY);
        // 创建一个 SIFT 特征提取器实例
        auto sift = cv::SIFT::create();
        // 对灰度图进行特征点检测与描述子计算，结果存储到 kp1（关键点）和 des1（描述子）
        sift->detectAndCompute(img1_gray, cv::noArray(), kp1, des1);
    }
}

void MainWindow::loadImage2() {
    // 弹出文件选择对话框，供用户选择图像2（第二张图），支持常见图像格式
    QString fileName = QFileDialog::getOpenFileName(this, "选择图像2", "", "Images (*.png *.jpg *.jpeg)");
    // 如果用户成功选择了文件（文件路径非空）
    if (!fileName.isEmpty()) {
        originalPixmap2 = QPixmap(fileName);        // 将选中的图像文件加载为 QPixmap，并保存到成员变量 originalPixmap2 中

        // 调整尺寸大小,如果尺寸的宽高大于1500,那么就除以2
        while (originalPixmap2.width() > MAXSIZE || originalPixmap2.height() > MAXSIZE){
            originalPixmap2 = originalPixmap2.scaled(
                    originalPixmap2.width() / 2, originalPixmap2.height() / 2,
                    Qt::KeepAspectRatio, Qt::SmoothTransformation);
        }

        cv::Mat img2 = QPixmapToCvMat(originalPixmap2);        // 将 QPixmap 转换为 OpenCV 可处理的 cv::Mat 类型（BGR 彩色图像）
        cv::cvtColor(img2, img2_gray, cv::COLOR_BGR2GRAY);        // 将图像转换为灰度图，用于 SIFT 特征提取（灰度能减少噪声和计算复杂度）

        auto sift = cv::SIFT::create();        // 创建 SIFT 特征提取器实例（OpenCV 4.x 推荐使用 create()）
        sift->detectAndCompute(img2_gray, cv::noArray(), kp2, des2); // 从图像2中提取关键点和描述子，存储在 kp2 和 des2 中
        if (!des1.empty()) {        // 如果图像1的描述子 des1 已经准备好了（即图像1也加载并提取过特征）
            cv::FlannBasedMatcher matcher;            // 创建 FlannBasedMatcher 匹配器（用于高维特征的高效近似匹配）
            std::vector<std::vector<cv::DMatch>> knnMatches;            // 定义存放 knn 匹配结果的容器，每个点找两个最邻近点
            matcher.knnMatch(des1, des2, knnMatches, 2); // 图1对图2进行 KNN 匹配
            // 清空旧的匹配数据（避免多次点击时数据叠加）
            goodMatches.clear();
            src_pts.clear();
            dst_pts.clear();
            // 对每一组匹配点执行 Lowe's ratio test 过滤错误匹配
            for (const auto& m : knnMatches) {
                // 如果找到两个最近邻，并且最近点距离远小于次近点（比例阈值 0.7）
                if (m.size() >= 2 && m[0].distance < 0.7f * m[1].distance) {
                    // 将通过测试的匹配添加到 goodMatches 中
                    goodMatches.push_back(m[0]);
                    // 提取匹配点对应的图像坐标：src 为图像1的关键点，dst 为图像2的关键点
                    src_pts.push_back(kp1[m[0].queryIdx].pt);
                    dst_pts.push_back(kp2[m[0].trainIdx].pt);
                }
            }

            // 如果匹配点数过少，弹出警告提示框（如无法估计变换矩阵）
            if (goodMatches.size() < 4) {
                QMessageBox::warning(this, "匹配失败", "匹配点不足，无法进行图像矫正。");
                // 即使匹配失败，也显示原始图像
                imageLabel2->setOriginalPixmap(originalPixmap2);
            } else {
                // 匹配成功，开始图像矫正对齐流程
                qDebug() << "匹配点数：" << goodMatches.size();
                try {
                    // 计算单应矩阵，将图2矫正到图1的坐标系
                    cv::Mat H = cv::findHomography(dst_pts, src_pts,
                                                   cv::RANSAC, 5.0);
                    
                    if (!H.empty() && H.rows == 3 && H.cols == 3) {
                        // 检查单应矩阵质量
                        double det = cv::determinant(H);
                        if (det < 0.1 || det > 10.0) {
                            QMessageBox::warning(this, "矫正警告", 
                                QString("图像差异较大（行列式值: %1），矫正效果可能不理想").arg(det));
                        }
                        // 获取图1的尺寸作为目标尺寸
                        cv::Size targetSize(img1_gray.cols, img1_gray.rows);
                        
                        // 对图2进行透视变换矫正
                        cv::Mat img2_corrected;
                        cv::warpPerspective(img2, img2_corrected, H, targetSize, 
                                          cv::INTER_LINEAR, cv::BORDER_CONSTANT,
                                          cv::Scalar(255, 255, 255));

                        // 将矫正后的图像转换回QPixmap
                        QImage qimg(img2_corrected.data, img2_corrected.cols,
                                    img2_corrected.rows,
                                   img2_corrected.step, QImage::Format_BGR888);

                        QPixmap correctedPixmap = QPixmap::fromImage(qimg.rgbSwapped());


                        // 更新显示的图像为矫正后的版本
                        originalPixmap2 = correctedPixmap;
                        imageLabel2->setOriginalPixmap(correctedPixmap);
                        // 更新灰度图用于后续处理
                        cv::cvtColor(img2_corrected, img2_gray, cv::COLOR_BGR2GRAY);

                        // 显示成功信息
                        qDebug() << "图像2已成功矫正对齐，尺寸统一为：" << targetSize.width
                                                            << "x" <<targetSize.height;
                        qDebug() << "单应矩阵行列式值：" << det;

                    } else {
                        QMessageBox::warning(this, "矫正失败", "计算单应矩阵失败，显示原始图像。");
                        imageLabel2->setOriginalPixmap(originalPixmap2);
                    }
                } catch (const cv::Exception& e) {
                    QMessageBox::critical(this, "矫正错误", 
                        QString("图像矫正过程中发生错误: %1\n将显示原始图像。").arg(e.what()));
                    imageLabel2->setOriginalPixmap(originalPixmap2);
                }
            }
        } else {
            // 如果图像1还未加载，直接显示图像2
            imageLabel2->setOriginalPixmap(originalPixmap2);
            QMessageBox::information(this, "提示", "请先加载图像1，然后重新加载图像2以进行自动矫正。");
        }
    }
}

// 在主界面中触发清空标注的操作
void MainWindow::clearButtonfunction() {
    imageLabel1->clearAll();
    imageLabel2->clearAll();
}

void MainWindow::matchAndMapPoints() {
    if (originalPixmap1.isNull() || originalPixmap2.isNull())
        return;    // 检查两张图片是否已加载
    // 获取用户手动标注的点和矩形框的起止点
    const std::vector<QPoint>& userPoints = imageLabel1->getPoints();
    QPoint rectStartPoint = imageLabel1->getRectStartPoint();
    QPoint rectEndPoint = imageLabel1->getRectEndPoint();

    // 计算单应矩阵，使用RANSAC鲁棒估计
    cv::Mat H = cv::findHomography(src_pts, dst_pts, cv::RANSAC);
    if (H.empty() || H.rows != 3 || H.cols != 3) {
        QMessageBox::warning(this, "单应矩阵失败", "计算单应矩阵失败。");
        return;
    }
    // 【修正】调用正确的函数名 getDranwingRect
    bool isDrawingRect = imageLabel1->getDranwingReaction();
    qDebug() << "isDrawingRect:"<<isDrawingRect<<endl;

    // 打印矩形框坐标
    if (!rectStartPoint.isNull() && !rectEndPoint.isNull()&&!imageLabel1->getDranwingReaction()&&!isDrawingRect) {
        qDebug() << "[图1] 已标注矩形区域：" << rectStartPoint << " -> " << rectEndPoint;
        // 构造图1的矩形角点
        std::vector<cv::Point2f> rectPoints1;
        rectPoints1.emplace_back(static_cast<float>(rectStartPoint.x()), static_cast<float>(rectStartPoint.y()));
        rectPoints1.emplace_back(static_cast<float>(rectEndPoint.x()), static_cast<float>(rectEndPoint.y()));

        // 准备用于存储图2对应点的容器
        std::vector<cv::Point2f> rectPoints2;
        try {
            // 使用单应矩阵 H 进行坐标变换
            cv::perspectiveTransform(rectPoints1, rectPoints2, H);

            // 检查变换结果
            if (rectPoints2.size() == 2) {
                QPoint mappedStart(static_cast<int>(rectPoints2[0].x), static_cast<int>(rectPoints2[0].y));
                QPoint mappedEnd(static_cast<int>(rectPoints2[1].x), static_cast<int>(rectPoints2[1].y));
                qDebug() << "[图2] 对应矩形区域：" << mappedStart << " -> " << mappedEnd;

                // 【修改】将矩形框映射到图2
                imageLabel2->setMappedRect(mappedStart, mappedEnd);
            } else {
                qDebug() << "[警告] 矩形区域在图2上的映射点数不为2";
            }
        } catch (const cv::Exception& e) {
            qDebug() << "[错误] 计算图2矩形坐标时发生异常: " << e.what();
        }
    }

}




// 调整尺寸大小
void MainWindow::resizeEvent(QResizeEvent* event) {
    QWidget::resizeEvent(event);
    if (!originalPixmap1.isNull()) {
        imageLabel1->setOriginalPixmap(originalPixmap1); // 让 ImageLabel 重新缩放
    }
    if (!originalPixmap2.isNull()) {
        imageLabel2->setOriginalPixmap(originalPixmap2);
    }
    QWidget::resizeEvent(event); // 不要漏掉基类调用
}


